/**
 * 索引类型签名
 * 大部分情况下，我们可以在使用对象前就确定对象的结构，并为对象添加类型
 * 当我们无法确定对象中有哪些属性（或者说对象中可以出现任意多个属性）
 * 此时，就需要使用到索引签名类型了
 * 使用 [key: string] 来约束改接口中允许出现的属性名称，这里表示的是只要是string类型的属性名称，都可以出现在对象中
 * 这样对象obj中就可以出现人一多的属性名
 * 这里key只是一个占位符
 */
interface AnyObject {
  [key: string]: number
}

let obj: AnyObject = {
  a: 1,
  b: 2
}

// 映射类型：基于旧类型创建新的类型（对象类型），减少重复，提升效率
/**
 * 映射类型是基于索引签名的，所以也需要使用 [ ]
 * Key in PropKeys 表示 Key 可以是 PropKeys 联合类型中的任意一个，类似于 forin(let k in obj)。
 * 映射类型只能在类型别名中使用，不能在接口中使用
 */
type PropKeys = 'x' | 'y' | 'z'
type Type1 = { x: number; y: number; z: number }
type Type2 = { [key in PropKeys]: number }

/**
 * 映射类型除了根据联合类型创建新类型之外，也可以根据对象来创建
 * 首先使用keyof获取到对象类型props中所有建的联合类型，即 'a'|'b'|'c'
 * 然后 key in  表示key可以是props所有键名中的任意一个
 * 其实前面的泛型工具类都是基于映射类型来实现的
 */
type Props = { a: number; b: string; c: boolean }
type Type3 = { [key in keyof Props]: number }

// 映射类型的新写法
// Props['a'] 表示查询类型 Props 中属性 'a' 对应的类型 number。所以，TypeA 的类型为 number。
// [] 中的属性必须存在于被查询类型中，否则就会报错。
// 可以同时查询多个索引的类型
type Type4 = Props['a' | 'b']
